package com.sprite.framework.entity.mapper.reolver;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.w3c.dom.Element;
import org.w3c.dom.Node;

import com.sprite.framework.entity.mapper.MapperException;
import com.sprite.framework.entity.mapper.reolver.node.MixedSqlNode;
import com.sprite.framework.entity.mapper.reolver.node.TextSqlNode;
import com.sprite.framework.entity.mapper.reolver.parser.ChooseNodeParser;
import com.sprite.framework.entity.mapper.reolver.parser.ForeachNodeParser;
import com.sprite.framework.entity.mapper.reolver.parser.IfNodeParser;
import com.sprite.framework.entity.mapper.reolver.parser.OtherwiseNodeParser;
import com.sprite.framework.entity.mapper.reolver.parser.ParamSqlNodeParser;
import com.sprite.framework.entity.mapper.reolver.parser.SetNodeParser;
import com.sprite.framework.entity.mapper.reolver.parser.StatementRefNodeParser;
import com.sprite.framework.entity.mapper.reolver.parser.TrimNodeParser;
import com.sprite.framework.entity.mapper.reolver.parser.WhenNodeParser;
import com.sprite.framework.entity.mapper.reolver.parser.WhereNodeParser;
import com.sprite.utils.UtilString;
import com.sprite.utils.text.GenericTokenParser;
import com.sprite.utils.text.TokenHandler;

public final class Parser {

	// key is tagName
	private static final Map<String, NodeParser> nodeParserMap = new HashMap<String, NodeParser>();
	
	// key is tagName
	private static final Map<String, NodeParser> nodeParserCustomer = new HashMap<String, NodeParser>();
	static{
		registerDefault(new ChooseNodeParser());
		registerDefault(new ForeachNodeParser());
		registerDefault(new IfNodeParser());
		registerDefault(new OtherwiseNodeParser());
		registerDefault(new SetNodeParser());
		registerDefault(new TrimNodeParser());
		registerDefault(new WhereNodeParser());
		registerDefault(new WhenNodeParser());
		registerDefault(new ParamSqlNodeParser());
		registerDefault(new StatementRefNodeParser());
	}
	
	public static void register(NodeParser parser){
		nodeParserCustomer.put(parser.tagName(), parser);
	}
	
	private static void registerDefault(NodeParser parser){
		nodeParserMap.put(parser.tagName(), parser);
	}
	
	public static SqlNode parser(Node element){
		if(element.getNodeType() == Element.TEXT_NODE){
			if(UtilString.isBlank(element.getTextContent())){
				return null;
			}
			return parserTextContent(element);
		}
		
		if(element.getNodeType() != Element.ELEMENT_NODE){
			throw new MapperException("not support node type:" + element.getNodeType());
		}
		NodeParser parser = gerParser(((Element)element).getTagName());
		return parser.parser((Element)element);
	}
	
	public static <T extends Node> SqlNode parserMix(List<T> nodeList){
		MixedSqlNode rootNode = new MixedSqlNode();
		for(Node node : nodeList){
			SqlNode sqlNode = parser(node);
			if(sqlNode != null){
				rootNode.append(sqlNode);
			}
		}
		return rootNode;
	}
	
	private static NodeParser gerParser(String tagName){
		NodeParser parser = nodeParserMap.get(tagName);
		if(parser == null){
			 parser = nodeParserCustomer.get(tagName);
		}
		
		if(parser == null){
			throw new MapperException("not found NodeParser of "+ tagName);
		}
		
		return parser;
	}
	
	public static SqlNode parserTextContent(Node element){
		if(element.getNodeType() != Element.TEXT_NODE){
			throw new MapperException();
		}
		
		final List<String> params = new LinkedList<String>();
		String text = new GenericTokenParser("#{", "}", new TokenHandler() {
			
			public String handleToken(String content) {
				params.add(content.trim());
				return "?";
			}
		}).parse(element.getTextContent().trim());
		
		return new TextSqlNode(text, params);
	}
	
	public static SqlNode parserTextContent(String textConent){
		
		final List<String> params = new LinkedList<String>();
		String text = new GenericTokenParser("#{", "}", new TokenHandler() {
			
			public String handleToken(String content) {
				params.add(content.trim());
				return "?";
			}
		}).parse(textConent);
		
		return new TextSqlNode(text, params);
	}
	
	public static String parserTextContent(String textConent, final List<String> outParams){
		
		return new GenericTokenParser("#{", "}", new TokenHandler() {
			
			public String handleToken(String content) {
				outParams.add(content.trim());
				return "?";
			}
		}).parse(textConent);
		
	}
}
